/////////////////////////////////////////////////////////////////////
////                                                             ////
////  FPU                                                        ////
////  Floating Point Unit (Double precision)                     ////
////                                                             ////
////  Author: David Lundgren                                     ////
////          davidklun@gmail.com                                ////
////                                                             ////
/////////////////////////////////////////////////////////////////////
////                                                             ////
//// Copyright (C) 2009 David Lundgren                           ////
////                  davidklun@gmail.com                        ////
////                                                             ////
//// This source file may be used and distributed without        ////
//// restriction provided that this copyright statement is not   ////
//// removed from the file and that any derivative work contains ////
//// the original copyright notice and the associated disclaimer.////
////                                                             ////
////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     ////
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   ////
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   ////
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      ////
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         ////
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    ////
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   ////
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        ////
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  ////
//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  ////
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  ////
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         ////
//// POSSIBILITY OF SUCH DAMAGE.                                 ////
////                                                             ////
/////////////////////////////////////////////////////////////////////


`timescale 1ns / 100ps


module fpu_sub( clk, rst, enable, opa, opb, fpu_op, sign, diff_2, exponent_2);
input		clk;
input		rst;
input		enable;
input	[63:0]	opa, opb;	  
input	[2:0]	fpu_op;
output		sign;
output	[55:0]	diff_2;
output	[10:0]	exponent_2;
		
reg [6:0] 	diff_shift;
reg [6:0] 	diff_shift_2;


reg   [10:0] exponent_a;
reg   [10:0] exponent_b;
reg   [51:0] mantissa_a;
reg   [51:0] mantissa_b;
reg   expa_gt_expb;
reg   expa_et_expb;
reg   mana_gtet_manb;
reg   a_gtet_b;
reg   sign;
reg   [10:0] exponent_small;
reg   [10:0] exponent_large;
reg   [51:0] mantissa_small;
reg   [51:0] mantissa_large;
reg   small_is_denorm;
reg   large_is_denorm;
reg   large_norm_small_denorm;
reg   small_is_nonzero;
reg   [10:0] exponent_diff;
reg   [54:0] minuend;
reg   [54:0] subtrahend;
reg   [54:0] subtra_shift;
wire   subtra_shift_nonzero = |subtra_shift[54:0];
wire   subtra_fraction_enable = small_is_nonzero & !subtra_shift_nonzero;
wire   [54:0] subtra_shift_2 = { 54'b0, 1'b1 };
reg   [54:0] subtra_shift_3;
reg   [54:0] diff;
reg   diffshift_gt_exponent;
reg   diffshift_et_55; // when the difference = 0
reg   [54:0] diff_1;
reg   [10:0] exponent;
reg   [10:0] exponent_2;
wire   in_norm_out_denorm = (exponent_large > 0) & (exponent== 0);
reg   [55:0] diff_2;
		

always @(posedge clk) 
	begin
		if (rst) begin
		exponent_a <= 0;
		exponent_b <= 0;
		mantissa_a <= 0;
		mantissa_b <= 0;
		expa_gt_expb <= 0;
		expa_et_expb <= 0;
		mana_gtet_manb <= 0;
   		a_gtet_b <= 0;
   		sign <= 0;
   		exponent_small  <= 0;
   		exponent_large  <= 0;
  		mantissa_small  <= 0;
   		mantissa_large  <= 0;
   		small_is_denorm <= 0;
   		large_is_denorm <= 0;
   		large_norm_small_denorm <= 0;
   		small_is_nonzero <= 0;
		exponent_diff <= 0;
		minuend <= 0;
		subtrahend <= 0;
		subtra_shift <= 0;
		subtra_shift_3 <= 0;
		diff_shift_2 <= 0;
		diff <= 0;
		diffshift_gt_exponent <= 0;
		diffshift_et_55 <= 0;
		diff_1 <= 0;
		exponent <= 0;
		exponent_2 <= 0;
		diff_2 <= 0;
		end
		else if (enable) begin
		exponent_a <= opa[62:52];
		exponent_b <= opb[62:52];
		mantissa_a <= opa[51:0];
		mantissa_b <= opb[51:0];
		expa_gt_expb <= exponent_a > exponent_b;
		expa_et_expb <= exponent_a == exponent_b;
		mana_gtet_manb <= mantissa_a >= mantissa_b;
   		a_gtet_b <= expa_gt_expb | (expa_et_expb & mana_gtet_manb);
   		sign <= a_gtet_b ? opa[63] :!opb[63] ^ (fpu_op == 3'b000);
   		exponent_small  <= a_gtet_b ? exponent_b : exponent_a;
   		exponent_large  <= a_gtet_b ? exponent_a : exponent_b;
  		mantissa_small  <= a_gtet_b ? mantissa_b : mantissa_a;
   		mantissa_large  <= a_gtet_b ? mantissa_a : mantissa_b;
   		small_is_denorm <= !(exponent_small > 0);
   		large_is_denorm <= !(exponent_large > 0);
   		large_norm_small_denorm <= (small_is_denorm == 1 && large_is_denorm == 0);
   		small_is_nonzero <= (exponent_small > 0) | |mantissa_small[51:0];
		exponent_diff <= exponent_large - exponent_small - large_norm_small_denorm;
		minuend <= { !large_is_denorm, mantissa_large, 2'b00 };
		subtrahend <= { !small_is_denorm, mantissa_small, 2'b00 };
		subtra_shift <= subtrahend >> exponent_diff;
		subtra_shift_3 <= subtra_fraction_enable ? subtra_shift_2 : subtra_shift;
		diff_shift_2 <= diff_shift;
		diff <= minuend - subtra_shift_3;
		diffshift_gt_exponent <= diff_shift_2 > exponent_large;
		diffshift_et_55 <= diff_shift_2 == 55; 
		diff_1 <= diffshift_gt_exponent ? diff << exponent_large : diff << diff_shift_2;
		exponent <= diffshift_gt_exponent ? 0 : (exponent_large - diff_shift_2);
		exponent_2 <= diffshift_et_55 ? 0 : exponent;
		diff_2 <= in_norm_out_denorm ? { 1'b0, diff_1 >> 1} : {1'b0, diff_1};
		
		end
	end

	
always @(diff)
   casex(diff)	
    55'b1??????????????????????????????????????????????????????: diff_shift <=  0;
	55'b01?????????????????????????????????????????????????????: diff_shift <=  1;
	55'b001????????????????????????????????????????????????????: diff_shift <=  2;
	55'b0001???????????????????????????????????????????????????: diff_shift <=  3;
	55'b00001??????????????????????????????????????????????????: diff_shift <=  4;
	55'b000001?????????????????????????????????????????????????: diff_shift <=  5;
	55'b0000001????????????????????????????????????????????????: diff_shift <=  6;
	55'b00000001???????????????????????????????????????????????: diff_shift <=  7;
	55'b000000001??????????????????????????????????????????????: diff_shift <=  8;
	55'b0000000001?????????????????????????????????????????????: diff_shift <=  9;
	55'b00000000001????????????????????????????????????????????: diff_shift <=  10;
	55'b000000000001???????????????????????????????????????????: diff_shift <=  11;
	55'b0000000000001??????????????????????????????????????????: diff_shift <=  12;
	55'b00000000000001?????????????????????????????????????????: diff_shift <=  13;
	55'b000000000000001????????????????????????????????????????: diff_shift <=  14;
	55'b0000000000000001???????????????????????????????????????: diff_shift <=  15;
	55'b00000000000000001??????????????????????????????????????: diff_shift <=  16;
	55'b000000000000000001?????????????????????????????????????: diff_shift <=  17;
	55'b0000000000000000001????????????????????????????????????: diff_shift <=  18;
	55'b00000000000000000001???????????????????????????????????: diff_shift <=  19;
	55'b000000000000000000001??????????????????????????????????: diff_shift <=  20;
	55'b0000000000000000000001?????????????????????????????????: diff_shift <=  21;
	55'b00000000000000000000001????????????????????????????????: diff_shift <=  22;
	55'b000000000000000000000001???????????????????????????????: diff_shift <=  23;
	55'b0000000000000000000000001??????????????????????????????: diff_shift <=  24;
	55'b00000000000000000000000001?????????????????????????????: diff_shift <=  25;
	55'b000000000000000000000000001????????????????????????????: diff_shift <=  26;
	55'b0000000000000000000000000001???????????????????????????: diff_shift <=  27;
	55'b00000000000000000000000000001??????????????????????????: diff_shift <=  28;
	55'b000000000000000000000000000001?????????????????????????: diff_shift <=  29;
	55'b0000000000000000000000000000001????????????????????????: diff_shift <=  30;
	55'b00000000000000000000000000000001???????????????????????: diff_shift <=  31;
	55'b000000000000000000000000000000001??????????????????????: diff_shift <=  32;
	55'b0000000000000000000000000000000001?????????????????????: diff_shift <=  33;
	55'b00000000000000000000000000000000001????????????????????: diff_shift <=  34;
	55'b000000000000000000000000000000000001???????????????????: diff_shift <=  35;
	55'b0000000000000000000000000000000000001??????????????????: diff_shift <=  36;
	55'b00000000000000000000000000000000000001?????????????????: diff_shift <=  37;
	55'b000000000000000000000000000000000000001????????????????: diff_shift <=  38;
	55'b0000000000000000000000000000000000000001???????????????: diff_shift <=  39;
	55'b00000000000000000000000000000000000000001??????????????: diff_shift <=  40;
	55'b000000000000000000000000000000000000000001?????????????: diff_shift <=  41;
	55'b0000000000000000000000000000000000000000001????????????: diff_shift <=  42;
	55'b00000000000000000000000000000000000000000001???????????: diff_shift <=  43;
	55'b000000000000000000000000000000000000000000001??????????: diff_shift <=  44;
	55'b0000000000000000000000000000000000000000000001?????????: diff_shift <=  45;
	55'b00000000000000000000000000000000000000000000001????????: diff_shift <=  46;
	55'b000000000000000000000000000000000000000000000001???????: diff_shift <=  47;
	55'b0000000000000000000000000000000000000000000000001??????: diff_shift <=  48;
    55'b00000000000000000000000000000000000000000000000001?????: diff_shift <=  49;
	55'b000000000000000000000000000000000000000000000000001????: diff_shift <=  50;
	55'b0000000000000000000000000000000000000000000000000001???: diff_shift <=  51;
	55'b00000000000000000000000000000000000000000000000000001??: diff_shift <=  52;
	55'b000000000000000000000000000000000000000000000000000001?: diff_shift <=  53;
	55'b0000000000000000000000000000000000000000000000000000001: diff_shift <=  54;
	55'b0000000000000000000000000000000000000000000000000000000: diff_shift <=  55;
	default: diff_shift <= 0;
	endcase	

	
	

endmodule
